React.createElement

本质上讲,JSX 只是 React.createElement(component, props, ...children) 的语法糖(JSX 在线Babel编译器)。因此,在用到 JSX 时,即使并没有用到React,作用域中也必须包含 React 库:import React from 'react'。例如:

  1. 写函数组件时,

    function Welcome(props) {
      return <h1>Hello, {props.name}</h1>;
    }
  2. 配置文件 Object 中包含 JSX:

    {
      ...
      render: (data) => <div>{data}</div>
      ...
    }

为什么 JSX 中的组件名要以大写字母开头?

小写字母开头: 内置组件,HTML 元素,例如 <div><span>,会编译成 React.createElement('div')
大写字母开头:自定义组件或 JS 文件中导入的组件,例如 <Foo /><MyComponents.DatePicker />,会编译成 React.createElement(Foo)
例外:在 JSX 中使用点语法(Dot Notation)表示一个 React 组件 <obj.component /> 会编译成 React.createElement(obj.component)
解释:JSX 语法依赖babel进行解析。在 @babel/plugin-transform-react-jsx 中,

const visitor = helper({
  pre(state) {
    const tagName = state.tagName;
    const args = state.args;
    if (t.react.isCompatTag(tagName)) {
      args.push(t.stringLiteral(tagName));
    } else {
      args.push(state.tagExpr);
    }
  },

  post(state, pass) {
    state.callee = pass.get("jsxIdentifier")();
  },

  throwIfNamespace: THROW_IF_NAMESPACE,
});
function isCompatTag(tagName) {
  return !!tagName && /^[a-z]/.test(tagName);
}

即小写字母开头的 tagName 直接转为 string 字符串;

// returns a closure that returns an identifier or memberExpression node
// based on the given id
const createIdentifierParser = (id: string) => () => {
  return id
    .split(".")
    .map(name => t.identifier(name))
    .reduce((object, property) => t.memberExpression(object, property));
};

即在 JSX 中使用点语法(Dot Notation)时,不管是大写还是小写,最终都会解析成对象和属性,而不是字符串,也所以虽然 C[p] 和 C.p 一样,但是 JSX 中 <c.p /> 可以解析而 <c[p] /> 不行。

import React from 'react';
import { PhotoStory, VideoStory } from './stories';

const components = {
  photo: PhotoStory,
  video: VideoStory
};

function Story(props) {
  // 错误!JSX 类型不能是表达式
  return <components[props.storyType] story={props.story} />;

  // 正确!JSX 类型可以是一个以大写字母开头的变量.
  const SpecificStory = components[props.storyType];
  return <SpecificStory story={props.story} />;
}

JSX 是表达式且可以嵌入表达式

JSX 是表达式,编译后就变成常规的 Javascript 对象,所以可以

  • 给变量赋值:const button = <button>{name}</button>
  • 当做参数接收

    getFieldDecorator('price', {
      initialValue: { number: 0, currency: 'rmb' },
      rules: [{ validator: this.checkPrice }],
    })(<PriceInput />)
  • 作为函数的返回值: render: () => <Foo />

JSX props 可以用 Javascript 表达式或字符串字面量。props.children(标签中的内容) 是个特殊的 props。
在 JavaScript 中,if 语句和 for 循环不是表达式,因此不能在 JSX 中直接使用。

  • JavaScript 逻辑 && 运算符替代 if
  • 条件操作符 condition ? true : false 替代 if-else
  • map() 函数替代 for 循环

falsy 值渲染

falsy 是在 Boolean 上下文中已认定可转换为‘假’的值。

if (false)
if (null)
if (undefined)
if (0)
if (NaN)
if ('')
if ("")
if (document.all)

false,null,undefined,和 true 是有效的children,但是不会被渲染;
falsy 值例如 0,则会被渲染,例如:

// props.messages 是空数组时,props.messages.length 为 0 会被渲染出来。
<div>
  {props.messages.length &&
    <MessageList messages={props.messages} />
  }
</div>

应该改成:

<div>
  {props.messages.length > 0 &&
    <MessageList messages={props.messages} />
  }
</div>

Jane_Shen
629 声望4 粉丝